// Copyright (c) 2000 Just Objects B.V. <just@justobjects.nl>
// Distributable under LGPL license. See terms of license at gnu.org.

package nl.justobjects.pushlet.core;

import com.ecomm.chat.manager.ChatListenManager;
import com.ecomm.chat.manager.ChatThread;
import com.ecomm.chat.pojo.ChatMessage;
import com.ecomm.util.DateUtils;
import com.ecomm.util.StringUtils;
import java.io.IOException;

/**
 * Handles servlet requests from client.
 *
 * @version $Id: Controller.java,v 1.8 2005/02/28 15:58:05 justb Exp $
 * @author Just van den Broecke - Just Objects &copy;
 **/
public class Controller implements Protocol, ConfigDefs {
  
  private Session session;
  
  public Controller(Session theSession) {
    session = theSession;
  }
  
  /** Handle command. */
  public void doCommand(Command aCommand) {
    try {
      // Update lease time to live
      session.kick();
      
      // Set remote IP address of client
      session.setAddress(aCommand.httpReq.getRemoteAddr());
      
      debug("doCommand() event=" + aCommand.reqEvent);
      
      // Get event type
      String eventType = aCommand.reqEvent.getEventType();
      
      // Determine action based on event type
      if (eventType.equals(Protocol.E_REFRESH)) {
        // Pull/poll mode clients that refresh
        doRefresh(aCommand);
      } else if (eventType.equals(Protocol.E_SUBSCRIBE)) {
        // Subscribe
        doSubscribe(aCommand);
      } else if (eventType.equals(Protocol.E_UNSUBSCRIBE)) {
        // Unsubscribe
        doUnsubscribe(aCommand);
      } else if (eventType.equals(Protocol.E_JOIN)) {
        // Join
        doJoin(aCommand);
      } else if (eventType.equals(Protocol.E_JOIN_LISTEN)) {
        // Join and listen (for simple and e.g. REST apps)
        doJoinListen(aCommand);
      } else if (eventType.equals(Protocol.E_LEAVE)) {
        // Leave
        doLeave(aCommand);
      } else if (eventType.equals(Protocol.E_HEARTBEAT)) {
        // Heartbeat mainly to do away with browser "busy" cursor
        doHeartbeat(aCommand);
      } else if (eventType.equals(Protocol.E_PUBLISH)) {
        // Publish event
        doPublish(aCommand);
      } else if (eventType.equals(Protocol.E_LISTEN)) {
        // Listen to pushed events
        doListen(aCommand);
        //System.out.println("listen-----------------------");
      }
      
      // Handle response back to client
      if (eventType.endsWith(Protocol.E_LISTEN) ||
          eventType.equals(Protocol.E_REFRESH)) {
        // Data channel events
        // Loops until refresh or connection closed
        getSubscriber().fetchEvents(aCommand);
        
      } else {
        // Send response for control commands
        sendControlResponse(aCommand);
      }
      
    } catch (Throwable t) {
      warn("Exception in doCommand(): " + t);
      t.printStackTrace();
    }
  }
  
  public String toString() {
    return session.toString();
  }
  
  /** Handle heartbeat event. */
  protected void doHeartbeat(Command aCommand) {
    
    // Set heartbeat acknowledgement to client
    aCommand.setResponseEvent(new Event(E_HEARTBEAT_ACK));
  }
  
  /** Handle Join request. */
  protected void doJoin(Command aCommand) throws IOException {
    
    Event responseEvent = null;
    
    try {
      
      session.start();
      
      // Determine format for encoding Events to client.
      // Default assume a userAgent window on the other end.
      String format = aCommand.reqEvent.getField(P_FORMAT, FORMAT_JAVASCRIPT);
      
      session.setFormat(format);
      responseEvent = new Event(E_JOIN_ACK);
      //System.out.println(" ppliang  "+format );//enter
      // Set unique subscriber id and encoding format
      responseEvent.setField(P_ID, session.getId());
      responseEvent.setField(P_FORMAT, format);
      info("joined");
    } catch (Throwable t) {
      session.stop();
      responseEvent = new Event(E_NACK);
      responseEvent.setField(P_ID, session.getId());
      responseEvent.setField(P_REASON, "unexpected error: " + t);
      warn("doJoin() error: " + t);
      t.printStackTrace();
    } finally {
      // Always set response event in command
      aCommand.setResponseEvent(responseEvent);
    }
    
  }
  
  /** Handle JoinListen request. */
  protected void doJoinListen(Command aCommand) throws IOException {
    
    // Basically bundles a join and a listen
    // This request is handly for simple apps that
    // need to do a single request to get events immediately
    // For example in RESTful apps.
    
    // First do regular join
    doJoin(aCommand);
    if (!aCommand.getResponseEvent().getEventType().equals(E_NACK)) {
      // If successful do the listen
      doListen(aCommand);
      if (!aCommand.getResponseEvent().getEventType().equals(E_NACK)) {
        // If still ok do the listen ack
        aCommand.getResponseEvent().setField(P_EVENT, E_JOIN_LISTEN_ACK);
      }
    }
  }
  
  /** Handle Leave request. */
  protected void doLeave(Command aCommand) throws IOException {
    
    Event responseEvent = null;
    
    try {
      // Remove all subscriptions
      getSubscriber().bailout();
      
      // Prepare acknowledgement
      responseEvent = new Event(E_LEAVE_ACK);
      
      // Set unique subscriber id
      responseEvent.setField(P_ID, session.getId());
      info("left");
    } catch (Throwable t) {
      responseEvent = new Event(E_NACK);
      responseEvent.setField(P_ID, session.getId());
      responseEvent.setField(P_REASON, "unexpected error: " + t);
      warn("doLeave() error: " + t);
      t.printStackTrace();
    } finally {
      // Always set response event in command
      aCommand.setResponseEvent(responseEvent);
    }
    
  }
  
  /** Handle Listen request. */
  protected void doListen(Command aCommand) throws IOException {
    
    
    String mode = MODE_PULL;
    
    
    getSubscriber().setMode(mode);
    
    // Prepare acknowledgement
    Event listenAckEvent = new Event(E_LISTEN_ACK);
    
    // Add subscription(s) if subject(s) specified
    String subject = aCommand.reqEvent.getField(P_SUBJECT);
    if (subject != null) {
      
      // Add a subscription
      Subscription subscription = getSubscriber().addSubscription(subject, null);
      
      // Add subscription id and optional label to listen-ack event
      listenAckEvent.setField(P_SUBSCRIPTION_ID, subscription.getId());
    }
    
    // Set unique subscriber id, push mode and encoding format
    listenAckEvent.setField(P_ID, session.getId());
    listenAckEvent.setField(P_MODE, mode);
    listenAckEvent.setField(P_FORMAT, session.getFormat());
    
    // Activate the subscriber
    getSubscriber().activate();
    
    // Enqueue listen ack event on data channel
    aCommand.setResponseEvent(listenAckEvent);
    
    info("Listening mode=" + mode + " userAgent=" + session.getUserAgent());
    
  }
  
  /** Handle Publish request. */
  protected void doPublish(Command aCommand) {
    Event responseEvent = null;
    
    try {
      String subject = aCommand.reqEvent.getField(Protocol.P_SUBJECT);
      if (subject == null) {
        // Return error response
        responseEvent = new Event(E_NACK);
        responseEvent.setField(P_ID, session.getId());
        responseEvent.setField(P_REASON, "no subject provided");
      } else {
        aCommand.reqEvent.setField(P_FROM, session.getId());
        aCommand.reqEvent.setField(P_EVENT, E_DATA);
        
        // Event may be targeted to specific user (p_to field)
        String to = aCommand.reqEvent.getField(P_TO);
        if (to != null) {
          Dispatcher.getInstance().unicast(aCommand.reqEvent, to);
        } else {
          // No to: multicast
          debug("doPublish() event=" + aCommand.reqEvent);
          Dispatcher.getInstance().multicast(aCommand.reqEvent);
        }
        // 保存消息，皮俊良 5.14 新增
        //event={p_id=mijuqasete, action=send, msg=nihao, p_from=mijuqasete, nick=0000, p_time=1176533188, p_subject=/chatf96f28f88a16, p_event=data}
        if(aCommand.reqEvent.getField("action") !=null && aCommand.reqEvent.getField("action").equals("send")){
          saveChat(aCommand);
        }else  if(aCommand.reqEvent.getField("action") !=null && aCommand.reqEvent.getField("action").equals("enter")){
          new ChatThread(subject,"enter",aCommand.reqEvent.getField("nick")).start();//调用消息接口
        }else  if(aCommand.reqEvent.getField("action") !=null && aCommand.reqEvent.getField("action").equals("exit")){
          new ChatThread(subject,"exit",aCommand.reqEvent.getField("nick")).start();//调用消息接口
        }
        
        // Acknowledge
        responseEvent = new Event(E_PUBLISH_ACK);
      }
      
    } catch (Throwable t) {
      responseEvent = new Event(E_NACK);
      responseEvent.setField(P_ID, session.getId());
      responseEvent.setField(P_REASON, "unexpected error: " + t);
      warn("doPublish() error: " + t);
      t.printStackTrace();
    } finally {
      // Always set response event in command
      aCommand.setResponseEvent(responseEvent);
    }
  }
  private void saveChat(Command aCommand){
    String subject = aCommand.reqEvent.getField(Protocol.P_SUBJECT);
    ChatMessage chatmess = new ChatMessage();
    
    if(aCommand.reqEvent.getField("msg") !=null){
      chatmess.setMessage(StringUtils.deCode(aCommand.reqEvent.getField("msg"),"ISO8859-1","GBK"));
      chatmess.setSenduserid(aCommand.reqEvent.getField("nick"));
      chatmess.setParentid(subject);
      if(subject.indexOf("chat") >= 0){
        chatmess.setType("1");
      }else{
        chatmess.setType("2");
      }
      chatmess.setBigtype("2");
      chatmess.setSendtime(DateUtils.getCurrTime());
      new ChatThread(chatmess,"send").start();//调用消息接口
    }else{
     // System.out.println("msg can not null ");
    }
  }
  /** Handle refresh event. */
  protected void doRefresh(Command aCommand) {
    // Set ack
    aCommand.setResponseEvent(new Event(E_REFRESH_ACK));
  }
  
  /** Handle Subscribe request. */
  protected void doSubscribe(Command aCommand) throws IOException {
    
    Event responseEvent = null;
    try {
      String subject = aCommand.reqEvent.getField(Protocol.P_SUBJECT);
      Subscription subscription = null;
      if (subject == null) {
        // Return error response
        responseEvent = new Event(E_NACK);
        responseEvent.setField(P_ID, session.getId());
        responseEvent.setField(P_REASON, "no subject provided");
      } else {
        
        String label = aCommand.reqEvent.getField(Protocol.P_SUBSCRIPTION_LABEL);
        subscription = getSubscriber().addSubscription(subject, label);
        
        // Acknowledge
        responseEvent = new Event(E_SUBSCRIBE_ACK);
        responseEvent.setField(P_ID, session.getId());
        responseEvent.setField(P_SUBJECT, subject);
        responseEvent.setField(P_SUBSCRIPTION_ID, subscription.getId());
        if (label != null) {
          responseEvent.setField(P_SUBSCRIPTION_LABEL, label);
        }
        info("subscribed to " + subject + " sid=" + subscription.getId());
      }
      
    } catch (Throwable t) {
      responseEvent = new Event(E_NACK);
      responseEvent.setField(P_ID, session.getId());
      responseEvent.setField(P_REASON, "unexpected error: " + t);
      warn("doSubscribe() error: " + t);
      t.printStackTrace();
    } finally {
      // Always set response event in command
      aCommand.setResponseEvent(responseEvent);
    }
  }
  
  /** Handle Unsubscribe request. */
  protected void doUnsubscribe(Command aCommand) throws IOException {
    
    
    Event responseEvent = null;
    try {
      String subscriptionId = aCommand.reqEvent.getField(Protocol.P_SUBSCRIPTION_ID);
      if (subscriptionId == null) {
        // Unsuscbribe all
        getSubscriber().removeSubscriptions();
        responseEvent = new Event(E_UNSUBSCRIBE_ACK);
        responseEvent.setField(P_ID, session.getId());
        info("unsubscribed all");
      } else {
        // Subscription id provided: remove Subscription
        Subscription subscription = getSubscriber().removeSubscription(subscriptionId);
        if (subscription == null) {
          // Unknown subscription id: return error response
          responseEvent = new Event(E_NACK);
          responseEvent.setField(P_ID, session.getId());
          responseEvent.setField(P_REASON, "no subscription for sid=" + subscriptionId);
          warn("unsubscribe: no subscription for sid=" + subscriptionId);
        } else {
          // OK return ack
          responseEvent = new Event(E_UNSUBSCRIBE_ACK);
          responseEvent.setField(P_ID, session.getId());
          responseEvent.setField(P_SUBSCRIPTION_ID, subscription.getId());
          responseEvent.setField(P_SUBJECT, subscription.getSubject());
          if (subscription.getLabel() != null) {
            responseEvent.setField(P_SUBSCRIPTION_LABEL, subscription.getLabel());
          }
          info("unsubscribed sid= " + subscriptionId);
        }
      }
    } catch (Throwable t) {
      responseEvent = new Event(E_NACK);
      responseEvent.setField(P_ID, session.getId());
      responseEvent.setField(P_REASON, "unexpected error: " + t);
      warn("doUnsubscribe() error: " + t);
      t.printStackTrace();
    } finally {
      // Always set response event in command
      aCommand.setResponseEvent(responseEvent);
    }
  }
  
  public Subscriber getSubscriber() {
    return session.getSubscriber();
  }
  
  /** Send response on the control channel. */
  protected void sendControlResponse(Command aCommand) {
    try {
      
      // Try to prevent caching in any form.
      aCommand.sendResponseHeaders();
      
      // Let clientAdapter determine how to send event
      aCommand.getClientAdapter().start();
      
      // Push to client through client adapter
      aCommand.getClientAdapter().push(aCommand.getResponseEvent());
      
      // One shot response
      aCommand.getClientAdapter().stop();
    } catch (Throwable t) {
      getSubscriber().bailout();
      return;
    }
  }
  
  
  /** Info. */
  protected void info(String s) {
    session.info("[Controller] " + s);
  }
  
  /** Exceptional print util. */
  protected void warn(String s) {
    session.warn("[Controller] " + s);
  }
  
  /** Exceptional print util. */
  protected void debug(String s) {
    session.debug("[Controller] " + s);
  }
  
  
}

/*
 * $Log: Controller.java,v $
 * Revision 1.8  2005/02/28 15:58:05  justb
 * added SimpleListener example
 *
 * Revision 1.7  2005/02/28 13:05:59  justb
 * introduced join-listen protocol service
 *
 * Revision 1.6  2005/02/28 12:45:59  justb
 * introduced Command class
 *
 * Revision 1.5  2005/02/28 09:14:55  justb
 * sessmgr/dispatcher factory/singleton support
 *
 * Revision 1.4  2005/02/25 15:13:00  justb
 * session id generation more robust
 *
 * Revision 1.3  2005/02/21 16:59:06  justb
 * SessionManager and session lease introduced
 *
 * Revision 1.2  2005/02/21 12:32:28  justb
 * fixed publish event in Controller
 *
 * Revision 1.1  2005/02/21 11:50:46  justb
 * ohase1 of refactoring Subscriber into Session/Controller/Subscriber
 *
 
 *
 */
